package org.peterf.foxish.math.transformation;

import org.peterf.foxish.math.FloatUtil;

import android.util.FloatMath;

public class Matrix2D {

    protected float m[] = new float[16];

    public void apply(float[] vec) {
        float x = m[0] * vec[0] + m[4] * vec[1] + m[12];
        float y = m[1] * vec[0] + m[5] * vec[1] + m[13];
        float w = m[3] * vec[0] + m[7] * vec[1] + m[15];
        vec[0] = x / w;
        vec[1] = y / w;
    }

    public float[] getMatrix() {
        return m;
    }
        
    public Matrix2D postRotation(float radian) {
        float sr = FloatUtil.sin(radian);
        float cr = FloatUtil.cos(radian);
        for (int i = 0; i < 16; i += 4) {
            float t = m[i];
            float u = m[i + 1];
            m[i] = t * cr - u * sr;
            m[i + 1] = u * cr + t * sr;
        }
        return this;
    }
    
    public Matrix2D preRotation(float radian) {
        float sr = FloatUtil.sin(radian);
        float cr = FloatUtil.cos(radian);
        for (int i = 0; i < 4; ++i) {
            float t = m[i];
            float u = m[i + 4];
            m[i] = t * cr + u * sr;
            m[i + 4] = u * cr - t * sr;
        }
        return this;
    }

    public Matrix2D postRotation(float radian, float px, float py) {
        float sr = FloatUtil.sin(radian);
        float cr = FloatUtil.cos(radian);
        for (int i = 0; i < 16; i += 4) {
            float t = m[i];
            float u = m[i + 1];
            m[i] = t * cr - u * sr - m[i + 3] * (px * (cr - 1) - py * sr);
            m[i + 1] = u * cr + t * sr - m[i + 3] * (py * (cr - 1) + px * sr);
        }
        return this;
    }
    
    public Matrix2D preRotation(float radian, float px, float py) {
        float sr = FloatUtil.sin(radian);
        float cr = FloatUtil.cos(radian);
        for (int i = 0; i < 4; ++i) {
            float t = m[i];
            float u = m[i + 4];
            m[i] = t * cr + u * sr;
            m[i + 4] = u * cr - t * sr;
            m[i + 12] -= t * (px * (cr - 1.0f) - py * sr) + u * (py * (cr - 1.0f) + px * sr);
        }
        return this;
    }

    public Matrix2D postScale(float x, float y) {
        for (int i = 0; i < 16; i += 4) {
            m[i] *= x;
            m[i + 1] *= y;
        }
        return this;
    }
    
    public Matrix2D preScale(float x, float y) {
        for (int i = 0; i < 4; ++i) {
            m[i] *= x;
            m[i + 4] *= y;
        }
        return this;
    }

    public Matrix2D postScale(float x, float y, float px, float py) {
        for (int i = 0; i < 16; i += 4) {
            m[i] = m[i] * x + m[i + 3] * (1.0f - x) * px;
            m[i + 1] = m[i + 1] * y + m[i + 3] * (1.0f - y) * py;
        }
        return this;
    }
    
    public Matrix2D preScale(float x, float y, float px, float py) {
        for (int i = 0; i < 4; ++i) {
            m[i + 12] = m[i] * px * (1.0f - x) + m[i + 4] * py * (1.0f - y);
            m[i] *= x;
            m[i + 4] *= y;
        }
        return this;
    }

    public Matrix2D postSkew(float x, float y) {
        float tx = FloatUtil.tan(x);
        float ty = FloatUtil.tan(y);
        for (int i = 0; i < 16; i += 4) {
            float t = m[i];
            float u = m[i + 1];
            m[i] = t + u * tx;
            m[i + 1] = u + t * ty;
        }
        return this;
    }
    
    public Matrix2D preSkew(float x, float y) {
        float tx = FloatUtil.tan(x);
        float ty = FloatUtil.tan(y);
        for (int i = 0; i < 4; ++i) {
            float t = m[i];
            float u = m[i + 4];
            m[i] = t + u * ty;
            m[i + 4] = u + t * tx;
        }
        return this;
    }

    public Matrix2D postSkew(float x, float y, float px, float py) {
        float tx = FloatUtil.tan(x);
        float ty = FloatUtil.tan(y);
        for (int i = 0; i < 16; i += 4) {
            float t = m[i];
            float u = m[i + 1];
            m[i] = t + u * tx - m[i + 2] * py * tx;
            m[i + 1] = u + t * ty - m[i + 2] * px * ty;
        }
        return this;
    }
    
    public Matrix2D preSkew(float x, float y, float px, float py) {
        float tx = FloatUtil.tan(x);
        float ty = FloatUtil.tan(y);
        for (int i = 0; i < 4; ++i) {
            float t = m[i];
            float u = m[i + 4];
            m[i] = t + u * ty;
            m[i + 4] = u + t * tx;
            m[i + 8] -= t * py * tx + u * px * ty;
        }
        return this;
    }

    public Matrix2D postTranslate(float x, float y) {
        for (int i = 0; i < 16; i += 4) {
            m[i] += m[i + 3] * x;
            m[i + 1] += m[i + 3] * y;
        }
        return this;
    }
    
    public Matrix2D preTranslate(float x, float y) {
        for (int i = 0; i < 4; ++i) {
            m[i + 12] += m[i] * x + m[i + 4] * y;
        }
        return this;
    }

    public Matrix2D setIdentity() {
        for (int i = 1; i <= 14; ++i) {
            m[i] = 0.0f;
        }
        m[0] = m[5] = m[10] = m[15] = 1.0f;
        return this;
    }

    public Matrix2D setRotation(float radian) {
        float sr = FloatUtil.sin(radian);
        float cr = FloatUtil.cos(radian);
        m[0] = m[5] = cr;
        m[1] = sr;
        m[4] = -sr;
        m[10] = m[15] = 1.0f;
        m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0.0f;
        return this;
    }

    public Matrix2D setRotation(float radian, float px, float py) {
        float sr = FloatUtil.sin(radian);
        float cr = FloatUtil.cos(radian);
        m[0] = m[5] = cr;
        m[1] = sr;
        m[4] = -sr;
        m[10] = m[15] = 1.0f;
        m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
        m[12] = sr * py + (1 - cr) * px;
        m[13] = -sr * px + (1 - cr) * py;
        return this;
    }

    public Matrix2D setScale(float x, float y) {
        m[0] = x;
        m[5] = y;
        m[10] = m[15] = 1.0f;
        m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0.0f;
        return this;
    }

    public Matrix2D setScale(float x, float y, float px, float py) {
        m[0] = x;
        m[5] = y;
        m[10] = m[15] = 1.0f;
        m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
        m[12] = (1.0f - x) * px;
        m[13] = (1.0f - y) * py;
        return this;
    }

    public Matrix2D setSkew(float x, float y) {
        m[0] = m[5] = m[10] = m[15] = 1.0f;
        m[4] = FloatUtil.tan(x);
        m[1] = FloatUtil.tan(y);
        m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[12] = m[13] = m[14] = 0.0f;
        return this;
    }

    public Matrix2D setSkew(float x, float y, float px, float py) {
        m[0] = m[5] = m[10] = m[15] = 1.0f;
        m[4] = FloatUtil.tan(x);
        m[1] = FloatUtil.tan(y);
        m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
        m[12] = -m[4] * py;
        m[13] = -m[1] * px;
        return this;
    }

    public Matrix2D setTranslate(float x, float y) {
        m[0] = m[5] = m[10] = m[15] = 1.0f;
        m[12] = x;
        m[13] = y;
        m[1] = m[2] = m[3] = m[4] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
        return this;
    }

}
